home *** CD-ROM | disk | FTP | other *** search
- /*----------------------------------------------------------------------*
- * IFFW.C Support routines for writing IFF-85 files. 1/23/86
- * (IFF is Interchange Format File.)
- *
- * By Jerry Morrison and Steve Shaw, Electronic Arts.
- * This software is in the public domain.
- *
- * This version for the Commodore-Amiga computer.
- *----------------------------------------------------------------------*/
- #include "iff/iff.h"
- #include "iff/gio.h"
- #include "proto/dos.h"
-
- /* ---------- IFF Writer -----------------------------------------------*/
-
- /* A macro to test if a chunk size is definite, i.e. not szNotYetKnown.*/
- #define Known(size) ( (size) != szNotYetKnown )
-
- /* Yet another weird macro to make the source code simpler...*/
- #define IfIffp(expr) {if (iffp == IFF_OKAY) iffp = (expr);}
-
- /* ---------- OpenWIFF -------------------------------------------------*/
-
- IFFP
- OpenWIFF (BPTR file, GroupContext *new0, LONG limit)
- {
- register GroupContext *new = new0;
- register IFFP iffp = IFF_OKAY;
-
- new->parent = NULL;
- new->clientFrame = NULL;
- new->file = file;
- new->position = 0;
- new->bound = limit;
- new->ckHdr.ckID = NULL_CHUNK; /* indicates no current chunk */
- new->ckHdr.ckSize = new->bytesSoFar = 0;
-
- if (0 > Seek (file, 0, OFFSET_BEGINNING)) /* Go to start of the file.*/
- iffp = DOS_ERROR;
- else if (Known (limit) && IS_ODD (limit))
- iffp = CLIENT_ERROR;
- return (iffp);
- }
-
- /* ---------- StartWGroup ----------------------------------------------*/
- IFFP
- StartWGroup (GroupContext *parent, ID groupType, LONG groupSize, ID subtype,
- GroupContext *new)
- {
- register IFFP iffp;
-
- iffp = PutCkHdr (parent, groupType, groupSize);
- IfIffp (IFFWriteBytes (parent, (BYTE *) & subtype, sizeof (ID)));
- IfIffp (OpenWGroup (parent, new));
- return (iffp);
- }
-
- /* ---------- OpenWGroup -----------------------------------------------*/
- IFFP
- OpenWGroup (GroupContext *parent0, GroupContext *new0)
- {
- register GroupContext *parent = parent0;
- register GroupContext *new = new0;
- register LONG ckEnd;
- register IFFP iffp = IFF_OKAY;
-
- new->parent = parent;
- new->clientFrame = parent->clientFrame;
- new->file = parent->file;
- new->position = parent->position;
- new->bound = parent->bound;
- new->ckHdr.ckID = NULL_CHUNK;
- new->ckHdr.ckSize = new->bytesSoFar = 0;
-
- if (Known (parent->ckHdr.ckSize))
- {
- ckEnd = new->position + ChunkMoreBytes (parent);
- if (new->bound == szNotYetKnown || new->bound > ckEnd)
- new->bound = ckEnd;
- };
-
- if (parent->ckHdr.ckID == NULL_CHUNK || /* not currently writing a chunk*/
- IS_ODD (new->position) ||
- (Known (new->bound) && IS_ODD (new->bound)))
- iffp = CLIENT_ERROR;
- return (iffp);
- }
-
- /* ---------- CloseWGroup ----------------------------------------------*/
- IFFP
- CloseWGroup (GroupContext *old0)
- {
- register GroupContext *old = old0;
- IFFP iffp = IFF_OKAY;
-
- if (old->ckHdr.ckID != NULL_CHUNK) /* didn't close the last chunk */
- iffp = CLIENT_ERROR;
- else if (old->parent == NULL)
- { /* top level file context */
- if (GWriteFlush (old->file) < 0)
- iffp = DOS_ERROR;
- }
- else
- { /* update parent context */
- old->parent->bytesSoFar += old->position - old->parent->position;
- old->parent->position = old->position;
- };
- return (iffp);
- }
-
- /* ---------- EndWGroup ------------------------------------------------*/
- IFFP
- EndWGroup (GroupContext *old)
- {
- register GroupContext *parent = old->parent;
- register IFFP iffp;
-
- iffp = CloseWGroup (old);
- IfIffp (PutCkEnd (parent));
- return (iffp);
- }
-
- /* ---------- PutCk ----------------------------------------------------*/
- IFFP
- PutCk (GroupContext *context, ID ckID, LONG ckSize, BYTE *data)
- {
- register IFFP iffp = IFF_OKAY;
-
- if (ckSize == szNotYetKnown)
- iffp = CLIENT_ERROR;
- IfIffp (PutCkHdr (context, ckID, ckSize));
- IfIffp (IFFWriteBytes (context, data, ckSize));
- IfIffp (PutCkEnd (context));
- return (iffp);
- }
-
- /* ---------- PutCkHdr -------------------------------------------------*/
- IFFP
- PutCkHdr (GroupContext *context0, ID ckID, LONG ckSize)
- {
- register GroupContext *context = context0;
- LONG minPSize = sizeof (ChunkHeader); /* physical chunk >= minPSize bytes*/
-
- /*
- * CLIENT_ERROR if we're already inside a chunk or asked to
- * write other than one FORM, LIST, or CAT at the top level of
- * a file.
- * Also, non-positive ID values are illegal and used for error
- * codes. (We could check for other illegal IDs...)
- */
- if (context->ckHdr.ckID != NULL_CHUNK || ckID <= 0)
- return (CLIENT_ERROR);
- else if (context->parent == NULL)
- {
- switch (ckID)
- {
- case FORM:
- case LIST:
- case CAT:
- break;
- default:
- return (CLIENT_ERROR);
- }
- if (context->position != 0)
- return (CLIENT_ERROR);
- }
-
- if (Known (ckSize))
- {
- if (ckSize < 0)
- return (CLIENT_ERROR);
- minPSize += ckSize;
- };
- if (Known (context->bound) &&
- context->position + minPSize > context->bound)
- return (CLIENT_ERROR);
-
- context->ckHdr.ckID = ckID;
- context->ckHdr.ckSize = ckSize;
- context->bytesSoFar = 0;
- if (0 > GWrite (context->file,
- (BYTE *) & context->ckHdr,
- sizeof (ChunkHeader)) )
- return (DOS_ERROR);
- context->position += sizeof (ChunkHeader);
- return (IFF_OKAY);
- }
-
- /* ---------- IFFWriteBytes ---------------------------------------------*/
- IFFP
- IFFWriteBytes (GroupContext *context0, BYTE *data, LONG nBytes)
- {
- register GroupContext *context = context0;
-
- if (context->ckHdr.ckID == NULL_CHUNK /* not in a chunk */
- || nBytes < 0 /* negative nBytes */
- || (Known (context->bound) /* overflow context */
- && context->position + nBytes > context->bound)
- || (Known (context->ckHdr.ckSize) /* overflow chunk */
- && context->bytesSoFar + nBytes > context->ckHdr.ckSize)
- )
- return (CLIENT_ERROR);
-
- if (0 > GWrite (context->file, data, nBytes))
- return (DOS_ERROR);
-
- context->bytesSoFar += nBytes;
- context->position += nBytes;
- return (IFF_OKAY);
- }
-
- /* ---------- PutCkEnd -------------------------------------------------*/
- IFFP
- PutCkEnd (GroupContext *context0)
- {
- register GroupContext *context = context0;
- WORD zero = 0; /* padding source */
-
- if (context->ckHdr.ckID == NULL_CHUNK) /* not in a chunk */
- return (CLIENT_ERROR);
-
- if (Known(context->ckHdr.ckSize))
- {
- /*
- * make sure the client wrote as many bytes as planned
- */
- if (context->ckHdr.ckSize != context->bytesSoFar)
- return (CLIENT_ERROR);
- }
-
- /*
- * go back and set the chunk size to bytesSoFar
- */
- else if (0 > GSeek (context->file,
- -(context->bytesSoFar + sizeof (LONG)),
- OFFSET_CURRENT) )
- return (DOS_ERROR);
-
- else if (0 > GWrite (context->file,
- (BYTE *) & context->bytesSoFar,
- sizeof (LONG)) )
- return (DOS_ERROR);
-
- else if (0 > GSeek (context->file,
- context->bytesSoFar,
- OFFSET_CURRENT) )
- return (DOS_ERROR);
-
- /*
- * Write a pad byte if needed to bring us up to an even
- * boundary. Since the context end must be even, and since we
- * haven't overwritten the context, if we're on an odd position
- * there must be room for a pad byte.
- */
-
- if (IS_ODD (context->bytesSoFar))
- {
- if (0 > GWrite (context->file, (BYTE *) & zero, 1))
- return (DOS_ERROR);
- context->position += 1;
- };
-
- context->ckHdr.ckID = NULL_CHUNK;
- context->ckHdr.ckSize = context->bytesSoFar = 0;
- return (IFF_OKAY);
- }
-